use core::ptr::null_mut;

use crate::{
    c::rtx_os::{
        osMutexAcquire, osMutexAttr_t, osMutexId_t, osMutexNew, osMutexPrioInherit,
        osMutexRecursive, osMutexRelease, osMutexRobust, osRtxMutex_t, osWaitForever,
    },
    error::{parse_error, Error},
    utils::AlignStore,
};

const CB_SIZE: usize = size_of::<osRtxMutex_t>();

pub struct Mutex {
    cb_mem: AlignStore<CB_SIZE>,
    id: osMutexId_t,
}

pub struct MutexGuard<'a> {
    m: &'a Mutex,
}

impl<'a> Drop for MutexGuard<'a> {
    fn drop(&mut self) {
        unsafe {
            _ = self.m.unsafe_unlock();
        }
    }
}

impl Mutex {
    pub const fn default() -> Self {
        Self {
            cb_mem: AlignStore::new(),
            id: null_mut(),
        }
    }

    pub unsafe fn new(&mut self, name: &str) {
        let attr = osMutexAttr_t {
            name: name.as_ptr() as *const i8,
            attr_bits: osMutexPrioInherit | osMutexRecursive | osMutexRobust,
            cb_mem: self.cb_mem.raw_ptr(),
            cb_size: CB_SIZE as u32,
        };
        self.id = osMutexNew(&attr);
        assert!(self.id != null_mut(), "mutex create fail");
    }

    pub fn lock<'a>(&'a self) -> Result<MutexGuard<'a>, Error> {
        unsafe { self.unsafe_lock()? };
        Ok( MutexGuard { m: self } )
    }

    pub unsafe fn unsafe_lock(&self) -> Result<(), Error> {
        let ret = osMutexAcquire(self.id, osWaitForever);
        parse_error((), ret)
    }

    pub unsafe fn unsafe_unlock(&self) -> Result<(), Error> {
        let ret = osMutexRelease(self.id);
        parse_error((), ret)
    }
}
